// Copyright (c) Appeon. All rights reserved.
// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. 
// You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using SnapObjects.Expressions;

namespace SnapObjects.Data
{
    internal class DefaultSortParser
    {
        private readonly string[] _ascs = { "asc", "ascending" };
        private readonly string[] _descs = { "desc", "descending" };

        private readonly string[] _ignorecases = { "OrdinalIgnoreCase",
            "CurrentCultureIgnoreCase", "InvariantCultureIgnoreCase" };

        private readonly string[] _casesensitives = { "Ordinal", "CurrentCulture",
            "InvariantCulture" };

        private readonly ICompilable _compiler;

        public DefaultSortParser(ICompilable compiler)
        {
            _compiler = compiler;
        }

        public IDictionary<LambdaDelegate, DefaultSortOptions> Parse(string expression)
        {
            var result = new Dictionary<LambdaDelegate, DefaultSortOptions>();

            var tokenizer = new DefaultSortTokenizer(expression);

            var orderList = new List<string>();
            orderList.AddRange(_ascs);
            orderList.AddRange(_descs);

            var caseList = new List<string>();
            caseList.AddRange(_ignorecases);
            caseList.AddRange(_casesensitives);
            var nextExpstr = String.Empty;

            while (tokenizer.MoveNext())
            {
                var builder = new StringBuilder();
                var order = "";
                var brackets = 0;
                var caseStr = String.Empty;

                do
                {
                    if (orderList.Contains(
                        tokenizer.Current, StringComparer.OrdinalIgnoreCase))
                    {
                        order = tokenizer.Current;
                    }
                    else if (caseList.Contains(
                        tokenizer.Current, StringComparer.OrdinalIgnoreCase))
                    {
                        caseStr = tokenizer.Current;

                        break;
                    }
                    else if (tokenizer.Current == "(")
                    {
                        if ("asc".Equals(order, StringComparison.OrdinalIgnoreCase))
                        {
                            builder.Append(order);
                            order = "";
                        }

                        builder.Append(tokenizer.Current);
                        brackets++;
                    }
                    else if (tokenizer.Current == ")")
                    {
                        builder.Append(tokenizer.Current);
                        brackets--;
                    }
                    else if (tokenizer.Current == "," && brackets == 0)
                    {
                        break;
                    }
                    else if (!String.IsNullOrWhiteSpace(order)
                        && String.IsNullOrWhiteSpace(tokenizer.Current))
                    {
                        break;
                    }
                    else
                    {
                        builder.Append(tokenizer.Current);
                    }
                } while (tokenizer.MoveNext());

                var expstr = String.IsNullOrWhiteSpace(order) ? builder.ToString().Replace(" ", "")
                    : builder.ToString().Trim();

                if (expstr.Length > 0)
                {
                    order = String.IsNullOrWhiteSpace(order) ? "asc" : order;
                    caseStr = String.IsNullOrWhiteSpace(caseStr) ? "OrdinalIgnoreCase" : caseStr;

                    var kv = this.ParseExpression(expstr, order.Trim(), caseStr);

                    result.Add(kv.Key, kv.Value);
                }
            }

            return result;
        }

        private KeyValuePair<LambdaDelegate, DefaultSortOptions> ParseExpression(string expression,
            string order, string caseStr)
        {
            var lambdaDelegate = _compiler.Compile(expression);

            var isAsc = _ascs.Contains(order, StringComparer.OrdinalIgnoreCase);

            var stringComparer = this.GetStringComparer(caseStr);

            return new KeyValuePair<LambdaDelegate, DefaultSortOptions>(
                lambdaDelegate, new DefaultSortOptions(isAsc, stringComparer));
        }

        private StringComparer GetStringComparer(string caseStr)
        {
            StringComparer stringComparer = null;

            if ("CurrentCulture".Equals(caseStr, StringComparison.CurrentCulture))
            {
                stringComparer = StringComparer.CurrentCulture;
            }
            else if ("CurrentCultureIgnoreCase".Equals(caseStr,
                StringComparison.CurrentCultureIgnoreCase))
            {
                stringComparer = StringComparer.CurrentCultureIgnoreCase;
            }
            else if ("InvariantCulture".Equals(caseStr, StringComparison.InvariantCulture))
            {
                stringComparer = StringComparer.InvariantCulture;
            }
            else if ("InvariantCultureIgnoreCase".Equals(caseStr, StringComparison.InvariantCultureIgnoreCase))
            {
                stringComparer = StringComparer.InvariantCultureIgnoreCase;
            }
            else if ("Ordinal".Equals(caseStr, StringComparison.Ordinal))
            {
                stringComparer = StringComparer.Ordinal;
            }
            else
            {
                stringComparer = StringComparer.OrdinalIgnoreCase;
            }

            return stringComparer;
        }
    }
}
